/*
 * Copyright © 2018 CODESTD.COM Inc. All rights reserved.
 */
package com.codestd.security.shiro.realm;

import com.codestd.security.mapper.SysUserMapper;
import com.codestd.security.model.SysUser;
import com.codestd.security.redis.model.RefreshToken;
import com.codestd.security.shiro.jwt.MobileTokenManager;
import com.codestd.security.shiro.token.StatelessToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Optional;

/**
 * 移动端登录
 *
 * @author jaune
 * @since 1.0.0
 */
public class MobileRealm extends DatabaseRealm {

    private MobileTokenManager mobileTokenManager;

    @Autowired
    private SysUserMapper sysUserMapper;

    public void setMobileTokenManager(MobileTokenManager mobileTokenManager) {
        this.mobileTokenManager = mobileTokenManager;
    }

    /**
     * 这里只处理StatelessToken
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof StatelessToken;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        StatelessToken statelessToken = (StatelessToken) token;
        // 判断是否有refreshToken，如果有使用refreshToken登录，如果没有就使用用户名密码登录。
        if (statelessToken.getRefreshToken() != null) {
            return this.loginByRefreshToken(statelessToken);
        } else {
            return this.loginByUsernameAndPassword(statelessToken);
        }
    }

    /**
     * 使用用户名密码登录
     */
    private AuthenticationInfo loginByUsernameAndPassword(StatelessToken statelessToken) {
        SysUser user = this.getAndCheckUser(statelessToken.getUsername());
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), user.getUsername());
        this.clearCachedAuthorizationInfo(authenticationInfo.getPrincipals());
        return authenticationInfo;
    }

    /**
     * 使用refreshToken登录。从缓存中获取refreshToken，如果缓存中有，则证明refreshToken有效可以作为获取AccessToken的凭证。
     */
    private AuthenticationInfo loginByRefreshToken(StatelessToken statelessToken) {
        Optional<RefreshToken> optional =
                this.mobileTokenManager.getRefreshToken(statelessToken.getRefreshToken());
        if (optional.isPresent()) {
            // RefreshToken缓存的时候，缓存了用户ID
            String userId = optional.get().getUserId();
            SysUser user = this.sysUserMapper.selectByPrimaryKey(userId);
            this.checkUser(user); // 这里同样要检查用户状态
            // 密码可以设置为空（第二个参数），我们在后面密码验证的时候会做处理
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, null, user.getUsername());
            this.clearCachedAuthorizationInfo(authenticationInfo.getPrincipals());
            return authenticationInfo;
        } else {
            return new SimpleAuthenticationInfo();
        }
    }
}
